// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现浮点的编码/解码。

package big

import (
	"encoding/binary"
	"fmt"
)

// Gob编解码器版本。允许对编码进行向后兼容的更改。
const floatGobVersion byte = 1

// GobEncode实现了gob。GobEncoder接口。
// 浮点值及其所有属性（精度、
// 舍入模式、精度）都被封送。
func (x *Float) GobEncode() ([]byte, error) {
	if x == nil {
		return nil, nil
	}

	// 确定编码
	sz := 1 + 1 + 4 // 版本+模式| acc |格式| neg（3+2+2+1bit）+prec 
	n := 0          // 尾数字数
	if x.form == finite {
		// 为尾数和指数添加空间
		n = int((x.prec + (_W - 1)) / _W) // 给定精度
		// 实际尾数片可以更短（0）或更短（0）较长的（未使用的位）：如果较短，只编码当前的单词如果较长，用字节编码时切掉未使用的字
		// （在实践中，这种情况永远不会发生，因为取整
		// 会处理它，但要安全，并且总是这样做）
		if len(x.mant) < n {
			n = len(x.mant)
		}
		// len（x.mant）>=n 
		sz += 4 + n*_S // exp+mant 
	}
	buf := make([]byte, sz)

	buf[0] = floatGobVersion
	b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1
	if x.neg {
		b |= 1
	}
	buf[1] = b
	binary.BigEndian.PutUint32(buf[2:], x.prec)

	if x.form == finite {
		binary.BigEndian.PutUint32(buf[6:], uint32(x.exp))
		x.mant[len(x.mant)-n:].bytes(buf[10:]) // 切掉未使用的尾随字
	}

	return buf, nil
}

// GoDecode实现gob。解码接口。
// 除非z的精度为0，否则结果将按照
// z的精度和舍入模式进行舍入，在这种情况下，z被精确设置为解码值
// 。
func (z *Float) GobDecode(buf []byte) error {
	if len(buf) == 0 {
		// 另一方发送了一个零或默认值。
		*z = Float{}
		return nil
	}

	if buf[0] != floatGobVersion {
		return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0])
	}

	oldPrec := z.prec
	oldMode := z.mode

	b := buf[1]
	z.mode = RoundingMode((b >> 5) & 7)
	z.acc = Accuracy((b>>3)&3) - 1
	z.form = form((b >> 1) & 3)
	z.neg = b&1 != 0
	z.prec = binary.BigEndian.Uint32(buf[2:])

	if z.form == finite {
		z.exp = int32(binary.BigEndian.Uint32(buf[6:]))
		z.mant = z.mant.setBytes(buf[10:])
	}

	if oldPrec != 0 {
		z.mode = oldMode
		z.SetPrec(uint(oldPrec))
	}

	return nil
}

// MarshalText实现了编码。TextMarshaler接口。
// 只封送浮点值（以全精度），其他
// 忽略精度或精度等属性。
func (x *Float) MarshalText() (text []byte, err error) {
	if x == nil {
		return []byte("<nil>"), nil
	}
	var buf []byte
	return x.Append(buf, 'g', -1), nil
}

// UnmarshalText实现了编码。TextUnmarshaler接口。
// 根据z的精度和舍入模式对结果进行舍入。
// 如果z的精度为0，则在舍入生效之前将其更改为64。
func (z *Float) UnmarshalText(text []byte) error {
	// TODO（gri）：摆脱[]字节/字符串转换
	_, _, err := z.Parse(string(text), 0)
	if err != nil {
		err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err)
	}
	return err
}
